package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面类，实现公共字段自动填充处理逻辑
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
  /**
   * 切入点: 拦截哪些方法
   */
  @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
  public void autoFillPointCut(){}
  
  /**
   * 前置通知，在通知中进行公共字段的赋值
   * @param joinPoint
   */
  @Before("autoFillPointCut()")
  public void autoFIll(JoinPoint joinPoint) throws Exception {
    log.info("开始进行公共字段自动填充...");
    // 1.获取当前被拦截的方法上的数据库操作类型
    MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象
    AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 方法上的注解对象
    OperationType operationType = autoFill.value(); // 数据库操作类型
    
    // 2.获取当前被拦截方法的参数 => 实体对象entry
    Object[] args = joinPoint.getArgs();
    Object entry = args[0];
    
    // 3.准备赋值的数据
    LocalDateTime now = LocalDateTime.now();
    Long id = BaseContext.getCurrentId();
    
    // 根据操作类型(insert/update) 来为实体对象的公共属性赋值 => 反射
    if (operationType == OperationType.INSERT) {
      // 为4个公共自动赋值
      Method setCreateTime = entry.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.class);
      Method setCreateUser = entry.getClass().getDeclaredMethod("setCreateUser", Long.class);
      Method setUpdateTime = entry.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
      Method setUpdateUser = entry.getClass().getDeclaredMethod("setUpdateUser", Long.class);
      
      // 通过反射为对象属性赋值
      setCreateTime.invoke(entry, now);
      setCreateUser.invoke(entry, id);
      setUpdateTime.invoke(entry, now);
      setUpdateUser.invoke(entry, id);
      
    } else if (operationType == OperationType.UPDATE) {
      // 为2个公共自动赋值
      Method setUpdateTime = entry.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
      Method setUpdateUser = entry.getClass().getDeclaredMethod("setUpdateUser", Long.class);
      
      // 通过反射为对象属性赋值
      setUpdateTime.invoke(entry, now);
      setUpdateUser.invoke(entry, id);
    }
    
  }
}
